/*
 *                         IndigoSCADA
 *
 *   This software and documentation are Copyright 2002 to 2014 Enscada 
 *   Limited and its licensees. All rights reserved. See file:
 *
 *                     $HOME/LICENSE 
 *
 *   for full copyright notice and license terms. 
 *
 */

#define INITGUID
#define _WIN32_DCOM
#include <windows.h>
#include <locale.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include "device.h"
#include "unilog.h"

#define LOGID logg,0
#define LOG_FNAME "opc_server_da.log"
#define CFG_FILE "opc_server_da.ini"

#include <opcda.h>
#include <opcerror.h>
#include "lightopc.h"

#define ECL_SID  "Enscada OPC server"// identificator of OPC server
#define PROG_ID "Enscada.OPC.Srv" //OPC Server ProgID

unilog *logg = NULL;

/**************************************************************************
			   OPC vendor info
**************************************************************************/
static const loVendorInfo vendor = {
  2 /*Major */ , 1 /*Minor */ , 10 /*Build */ , 0 /*Reserv */ ,
  ECL_SID
};

loService *our_service;
static int OPCstatus = OPC_STATUS_RUNNING;

#define TAGNAME_LEN 150

static DEV *devp;

static int driver_init(int lflags);
static void driver_destroy(void);
static void server_finished(void*, loService*, loClient*);

//Generated by GuidGen.exe
//-OPC Server CLSID

// {78BB6572-B8BA-47f9-83B3-4EC99DF9B1A3}
static const GUID GID_EnscadaOPCserverExe = 
{ 0x78bb6572, 0xb8ba, 0x47f9, { 0x83, 0xb3, 0x4e, 0xc9, 0x9d, 0xf9, 0xb1, 0xa3 } };


//*************************************************************************
class ourClassFactory: public IClassFactory
{
public:
  LONG RefCount;
  LONG server_count;
  CRITICAL_SECTION lk;  /* protect RefCount */
  
  ourClassFactory(): RefCount(0), server_count(0)
  {
    InitializeCriticalSection(&lk);
  }

  ~ourClassFactory()
  {
    DeleteCriticalSection(&lk);
  }

  //IUnknown
  STDMETHODIMP QueryInterface(REFIID, LPVOID*);
  STDMETHODIMP_(ULONG) AddRef( void);
  STDMETHODIMP_(ULONG) Release( void);
  // IClassFactory
  STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID*);
  STDMETHODIMP LockServer(BOOL);
  //
  inline LONG getRefCount(void)
  {
    LONG rc;
    EnterCriticalSection(&lk);
    rc = RefCount;
    LeaveCriticalSection(&lk);                                    
    return rc;
  }

  inline int in_use(void)
  {
    int rv;
    EnterCriticalSection(&lk);
    rv = RefCount | server_count;
    LeaveCriticalSection(&lk);
    return rv;
  }

  inline void serverAdd(void)
  {
    InterlockedIncrement(&server_count);
  }

  inline void serverRemove(void)
  {
    InterlockedDecrement(&server_count);
  }

};

/**************************************************************************
 IUnknown
**************************************************************************/
STDMETHODIMP ourClassFactory::QueryInterface(REFIID iid, LPVOID* ppInterface)
{
  if (ppInterface == NULL) return E_INVALIDARG;

  if (iid == IID_IUnknown || iid == IID_IClassFactory)
    {
      UL_DEBUG((LOGID, "ourClassFactory::QueryInterface() Ok"));
      *ppInterface = this;
      AddRef();
      return S_OK;
    }
  UL_DEBUG((LOGID, "ourClassFactory::QueryInterface() Failed"));

  *ppInterface = NULL;
  return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) ourClassFactory::AddRef(void)
{
  ULONG rv;
  EnterCriticalSection(&lk);
  rv = (ULONG)++RefCount;
  LeaveCriticalSection(&lk);                                    
  UL_DEBUG((LOGID, "ourClassFactory::AddRef(%ld)", rv));
  return rv;
}

STDMETHODIMP_(ULONG) ourClassFactory::Release(void)
{
  ULONG rv;
  EnterCriticalSection(&lk);
  rv = (ULONG)--RefCount;
  LeaveCriticalSection(&lk);
  UL_DEBUG((LOGID, "ourClassFactory::Release(%d)", rv));
  return rv;
}
/**************************************************************************
 IClassFactory
**************************************************************************/

STDMETHODIMP ourClassFactory::LockServer(BOOL fLock)
{
  if (fLock)
    AddRef();
  else
    Release();

  UL_DEBUG((LOGID, "ourClassFactory::LockServer(%d)", fLock)); 
  return S_OK;
}

STDMETHODIMP ourClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID* ppvObject)
{
  if (pUnkOuter != NULL)
    return CLASS_E_NOAGGREGATION; // Aggregation is not supported by this code

  IUnknown *server = 0;

  AddRef(); /* for server_finished() */
  if (loClientCreate(our_service, (loClient**)&server, 0, &vendor, server_finished, this))
    {
      UL_DEBUG((LOGID, "ourClassFactory::loCreateClient() failed"));
      Release();
      return E_OUTOFMEMORY;
    }
  serverAdd();

  HRESULT hr = server->QueryInterface(riid, ppvObject);
  if (FAILED(hr))
    {
      UL_DEBUG((LOGID, "ourClassFactory::loClient QueryInterface() failed"));
    }
  else
    {
      loSetState(our_service, (loClient*)server, loOP_OPERATE, OPCstatus, 0);
      UL_DEBUG((LOGID, "ourClassFactory::server_count = %ld", server_count));
    }
  server->Release();
  return hr;
}

/***************************************************************************
 EXE-specific stuff
***************************************************************************/

static ourClassFactory our_CF;

char argv0[FILENAME_MAX + 32];

#ifdef __GNUC__
#define  LLINT(x)  (x##LL)
#define ULLINT(x)  (x##ULL)
#endif
#ifdef _MSC_VER
#define  LLINT(x)  (x##i64)
#define ULLINT(x)  (x##ui64) /* or i64u ...*/
#endif

/* Offset between Windows epoch 1/1/1601 and
   Unix epoch 1/1/1970 in 100 nanosec units */

#define DELTA_EPOCH_IN_MICROSECS (LLINT(116444736000000000))

int gettimeofday(struct timeval *tv) {
    
    FILETIME file_time;
    LARGE_INTEGER place_holder;
    __int64 time;
    

    /* returns 64 bit value which is the number of 100 nanosecond
       intervals since 1601(UTC) */
    GetSystemTimeAsFileTime (&file_time);

    /* Windows recommends that we should copy the FILETIME returned 
       into a ULARGE_INTEGER and then perform the arithmetic on that */
    place_holder.LowPart = file_time.dwLowDateTime;
    place_holder.HighPart = file_time.dwHighDateTime;
    time = place_holder.QuadPart;
    time -= DELTA_EPOCH_IN_MICROSECS;

    /* Now we can use arithmetic operations on time which is nothing but
       a 64 bit integer holding time in 100 nanosec intervals */

    /* convert 100 nanoseconds intervals into microseconds .. divide by 10 */
    time /= 10;
    
    tv->tv_sec = (long)(time / 1000000);
    tv->tv_usec = (long)(time % 1000000);

    return 0;
}

static int adjust_hour = 1;

void epocToFileTime( signed __int64 t, LPFILETIME pft )
{
	int add_hour = 3600*adjust_hour;

    __int64 ll = t*LLINT(10000) + DELTA_EPOCH_IN_MICROSECS + Int32x32To64(add_hour, 10000000);
	
	//__int64 ll = t*LLINT(10000) + DELTA_EPOCH_IN_MICROSECS;

	/* returns 64 bit value which is the number of 100 nanosecond
    intervals since 1601(UTC) */
    pft->dwLowDateTime = (DWORD) ll;
    pft->dwHighDateTime = (unsigned long)(ll >>32);
}

char *logPath(char *fileName)
{
  static char path[MAX_PATH]="\0";
  
  strcpy(path, argv0);

  *(strrchr(path, '\\')) = '\0';        // Strip \\filename.exe off path
  *(strrchr(path, '\\')) = '\0';        // Strip \\bin off path

  strcat(path, "\\logs");
  strcat(path, "\\");
  strcat(path, fileName);

  return path;
}

static void server_finished(void *a, loService *b, loClient *c)
{
  our_CF.serverRemove();
  if (a) ((ourClassFactory*)a)->Release();
  UL_DEBUG((LOGID, "server_finished(%lu)", our_CF.server_count));
}

inline void init_common(void)
{
  logg = unilog_Create(ECL_SID, logPath(LOG_FNAME), NULL,
		      -2, /* Max filesize: -1 unlimited, -2 -don't change */
		      ll_DEBUG); /* level [ll_FATAL...ll_DEBUG] */
			  

  UL_INFO((LOGID, "Start"));
}

inline void cleanup_common(void)
{
  UL_INFO((LOGID, "Finish"));
  unilog_Delete(logg); logg = NULL;
}

//Interface to field//////////////////////////////////////////////////////////////////////////
#ifdef USE_RIPC_MIDDLEWARE
////////Middleware////////////
#include "RIPCThread.h"
#include "RIPCFactory.h"
#include "RIPCSession.h"
#include "RIPCServerFactory.h"
#include "RIPCClientFactory.h"
#include "ripc.h"
//////////////////////////////
#endif

#include "fifoc.h"
fifo_h fifo_control_direction; 
fifo_h fifo_monitor_direction; 
//////////////////////////////////////////////////////////////////////////////////////////////

#ifdef USE_RIPC_MIDDLEWARE
/////////////Middleware///////////////////////////////
RIPCFactory* factory1;
RIPCFactory* factory2;
RIPCSession* session1;
RIPCSession* session2;
RIPCQueue* queue_monitor_dir;	//queue in monitor direction: SCADA<------------- RTU
RIPCQueue* queue_control_dir;	//queue in control direction: SCADA-------------> RTU
#endif
int exit_threads;
int n_msg_sent_control_dir;
//////////////////////////////////////////////////////

////////////////////////////Middleware///////////////////////////////////////////////////////
#include "iec_item_type.h"
/////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////Middleware/////////////////////////
ORTEDomain              *domain;
ORTEPublication			*publisher;
ORTESubscription        *subscriber;
iec_item_type			instanceSend;
iec_item_type		    instanceRecv;
//////////////////////////////end//Middleware///////////

#include "iec104types.h"
#include "iec_item.h"
#include "clear_crc_eight.h"

//commands
void control_dir_consumer(void* pParam)
{
	unsigned char buf[sizeof(struct iec_item)];
    int len, rc;
	const unsigned wait_limit_ms = 1;
	struct iec_item* p_item;
	//////////////////iec 104 part //////////////////
	struct iec_item item_to_send;
	/////////////////////////////////////////////////

	while(1)
	{
		if(exit_threads)
		{
			break;
		}
	
		for(int i = 0; (len = fifo_get(fifo_control_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms)) >= 0; i += 1)
		{ 
			//Receive in control direction as iec 104 packet
			p_item = (struct iec_item*)buf;

			//for (int j = 0; j < len; j++) 
			//{ 
			//	unsigned char c = *((unsigned char*)buf + j);
				//printf("rx <--- 0x%02x-\n", c);

			//	IT_COMMENT1("rx <--- 0x%02x-\n", c);
			//}

			rc = clearCrc((unsigned char *)buf, sizeof(struct iec_item));

			if(rc != 0)
			{
				fprintf(stderr, "Error CRC8 = %d\n", rc);
				fflush(stderr);
				ExitProcess(0);
			}

			memcpy(&item_to_send, p_item, sizeof(struct iec_item));

			//unsigned char buf[sizeof(struct iec_item)];
			//int len = sizeof(struct iec_item);
			//memcpy(buf, &item_to_send, len);
			//	for(j = 0;j < len; j++)
			//	{
			//	  unsigned char c = *(buf + j);
				//fprintf(stderr,"tx ---> 0x%02x\n", c);
				//fflush(stderr);
				//IT_COMMENT1("tx ---> 0x%02x\n", c);
			//	}

			//Send in control direction as iec 104 packet
			fprintf(stderr,"Sending message %u th\n", n_msg_sent_control_dir);
			fflush(stderr);

			#ifdef USE_RIPC_MIDDLEWARE
			//publishing data
			queue_control_dir->put(&item_to_send, sizeof(struct iec_item));
			#endif

			//Send in monitor direction
			//prepare published data
			memset(&instanceSend,0x00, sizeof(iec_item_type));

			instanceSend.iec_type = item_to_send.iec_type;
			memcpy(&instanceSend.iec_obj, &(item_to_send.iec_obj), sizeof(struct iec_object));
			instanceSend.cause = item_to_send.cause;
			instanceSend.msg_id = item_to_send.msg_id;
			instanceSend.ioa_control_center = item_to_send.ioa_control_center;
			instanceSend.casdu = item_to_send.casdu;
			instanceSend.is_neg = item_to_send.is_neg;
			instanceSend.checksum = item_to_send.checksum;

			ORTEPublicationSend(publisher);
						
			n_msg_sent_control_dir++;
		}
	}
}

#ifdef USE_RIPC_MIDDLEWARE
void monitoring_dir_consumer(void* pParam)
{
	struct iec_item item;
	RIPCObject objDesc(&item, sizeof(struct iec_item));

	while(1)
	{
		if(exit_threads)
		{
			break;
		}
		
		queue_monitor_dir->get(objDesc);

		fifo_put(fifo_monitor_direction, (char*)&item, sizeof(struct iec_item));
	}
}
#endif

/////////////////////////////////////Middleware///////////////////////////////////////////
Boolean  quite = ORTE_FALSE;
int	regfail=0;

//event system
void onRegFail(void *param) 
{
  printf("registration to a manager failed\n");
  regfail = 1;
}

void rebuild_iec_item_message(struct iec_item *item2, iec_item_type *item1)
{
	unsigned char checksum;

	///////////////Rebuild struct iec_item//////////////////////////////////
	item2->iec_type = item1->iec_type;
	memcpy(&(item2->iec_obj), &(item1->iec_obj), sizeof(struct iec_object));
	item2->cause = item1->cause;
	item2->msg_id = item1->msg_id;
	item2->ioa_control_center = item1->ioa_control_center;
	item2->casdu = item1->casdu;
	item2->is_neg = item1->is_neg;
	item2->checksum = item1->checksum;
	///////and check the 1 byte checksum////////////////////////////////////
	checksum = clearCrc((unsigned char *)item2, sizeof(struct iec_item));

//	fprintf(stderr,"new checksum = %u\n", checksum);

	//if checksum is 0 then there are no errors
	if(checksum != 0)
	{
		//log error message
		ExitProcess(0);
	}

//	fprintf(stderr,"iec_type = %u\n", item2->iec_type);
//	fprintf(stderr,"iec_obj = %x\n", item2->iec_obj);
//	fprintf(stderr,"cause = %u\n", item2->cause);
//	fprintf(stderr,"msg_id =%u\n", item2->msg_id);
//	fprintf(stderr,"ioa_control_center = %u\n", item2->ioa_control_center);
//	fprintf(stderr,"casdu =%u\n", item2->casdu);
//	fprintf(stderr,"is_neg = %u\n", item2->is_neg);
//	fprintf(stderr,"checksum = %u\n", item2->checksum);
}

void recvCallBack(const ORTERecvInfo *info,void *vinstance, void *recvCallBackParam) 
{
	iec_item_type *item1 = (iec_item_type*)vinstance;

	switch (info->status) 
	{
		case NEW_DATA:
		{
		  if(!quite)
		  {
			  struct iec_item item2;
			  rebuild_iec_item_message(&item2, item1);
			  fifo_put(fifo_monitor_direction, (char*)&item2, sizeof(struct iec_item));
		  }
		}
		break;
		case DEADLINE:
		{
			//printf("deadline occurred\n");
		}
		break;
	}
}
////////////////////////////////Middleware/////////////////////////////////////


inline void cleanup_all(DWORD objid)
{
	if (FAILED(CoRevokeClassObject(objid)))
	UL_WARNING((LOGID, "CoRevokeClassObject() failed..."));
	driver_destroy();
	CoUninitialize();
	cleanup_common();

	exit_threads = 1;

	///IPC close///////////////////////
	fifo_close(fifo_monitor_direction);
	fifo_close(fifo_control_direction);
	///////////////////////////////////

	#ifdef USE_RIPC_MIDDLEWARE
	queue_monitor_dir->close();
	queue_control_dir->close();
	session1->close();
	session2->close();
	delete session1;
	delete session2;
	#endif
	///////////////////////////////////Middleware//////////////////////////////////////////////////
	ORTEDomainAppDestroy(domain);
	domain = NULL;
	////////////////////////////////////Middleware//////////////////////////////////////////////////
}

////////////////////////////apa///////////////////////////////////
DevRecord *DeviceDatabase;
#define MAX_RECORDS_IN_DEVICE_DB 25000
///////////////////////////////////////////////////////////////

#include <stdio.h>
#include <sqlite3.h>

static gl_row_counter = 0;
static gl_column_counter = 0;

static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
	int i;

	gl_column_counter = argc;
	
	for(i = 0; i < argc; i++)
	{
		fprintf(stderr, "%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
		fflush(stderr);

		switch(i)
		{
			case 0:
			{
				//colonna 1 nella tabella signals_measures_table
				//ItemID name
				strcpy(DeviceDatabase[gl_row_counter].name, argv[i]);
			}
			break;
			case 1:
			{
				//colonna 2 nella tabella signals_measures_table
				DeviceDatabase[gl_row_counter].dtype = atoi(argv[i]);

				if(strcmp(argv[i], "VT_I2") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_I2;
				}
				else if(strcmp(argv[i], "VT_UI2") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_UI2;
				}
				else if(strcmp(argv[i], "VT_R4") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_R4;
				}
				else if(strcmp(argv[i], "VT_BOOL") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_BOOL;
				}
				else if(strcmp(argv[i], "VT_UI4") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_UI4;
				}
				else if(strcmp(argv[i], "VT_BSTR") == 0)
				{
					DeviceDatabase[gl_row_counter].dtype = VT_BSTR;
				}
				else
				{
					fprintf(stderr,"OPC type %s from I/O list NOT supported\n", argv[i]);
					fflush(stderr);
					ExitProcess(0);
				}
			}
			break;
			case 2:
			{
				//colonna 3 nella tabella signals_measures_table
				DeviceDatabase[gl_row_counter].ioa_control_center = atoi(argv[i]);
			}
			break;
			default:
			break;
		}
	}

	//finito di leggere un record
	gl_row_counter++;

	fprintf(stderr, "\n");
	fflush(stderr);
	return 0;
}

static void iec_call_exit_handler(int line, char* file, char* reason);
static void poll_device(void);
static int opc_main(HINSTANCE hInstance, int argc, char *argv[]);
static int show_msg(LPCTSTR msg);
static int show_error(LPCTSTR msg);

extern "C" int main(int argc, char *argv[])
{
  return opc_main(GetModuleHandle(NULL), argc, argv);
}

#define SUPPLIER "@ enscada.com"
#define APPLICATION "opc_server_da.exe"

#define MAX_FIFO_SIZE 65535

int opc_main(HINSTANCE hInstance, int argc, char *argv[]) {
  const char eClsidName [] = ECL_SID;
  const char eProgID [] = PROG_ID;
  DWORD objid;
  char *cp;

  //version control///////////////////////////////////////////////////////////////
  char version[100];
  sprintf(version, ""APPLICATION" - Built on %s %s %s",__DATE__,__TIME__,SUPPLIER);
  fprintf(stderr, "%s\n", version);
  fflush(stderr);
  SYSTEMTIME oT;
  ::GetLocalTime(&oT);
  fprintf(stderr,"%02d/%02d/%04d, %02d:%02d:%02d Starting ... %s\n",oT.wMonth,oT.wDay,oT.wYear,oT.wHour,oT.wMinute,oT.wSecond,APPLICATION); 
  fflush(stderr);
  ////////////////////////////////////////////////////////////////////////////////

  objid=::GetModuleFileName(NULL, argv0, sizeof(argv0));
  if(objid==0 || objid+50 > sizeof(argv0)) return 0;

  init_common();

  if((cp = setlocale(LC_ALL, ".1251")) == NULL) 
  {
    UL_ERROR((LOGID, "setlocale() - Can't set 1251 code page"));
    cleanup_common();
    return 0;
  }

  cp = argv[1];
  if(cp) 
  {
    int finish = 1;

    if (strstr(cp, "/r")) 
	{
      if(loServerRegister(&GID_EnscadaOPCserverExe, eProgID, eClsidName, argv0, 0)) 
	  {
		show_error("Registration Failed");
		UL_ERROR((LOGID, "Registration <%s> <%s> Failed", eProgID, argv0));    
      } 
	  else 
	  {
		show_msg("Registration Ok");
		UL_INFO((LOGID, "Registration <%s> <%s> Ok", eProgID, argv0));    
      }
    } 
	else if (strstr(cp, "/u")) 
	{
      if (loServerUnregister(&GID_EnscadaOPCserverExe, eProgID)) 
	  {
		show_error("UnRegistration Failed");
		UL_ERROR((LOGID, "UnReg <%s> <%s> Failed", eClsidName, argv0));    
      } 
	  else 
	  {
		show_msg("Server Unregistered");
		UL_INFO((LOGID, "UnReg <%s> <%s> Ok", eClsidName, argv0));
      }
    } 
	else 
	{
      UL_WARNING((LOGID, "Ignore unknown option <%s>", cp));
      finish = 0;
    }

    if (finish) 
	{
      cleanup_common();
      return 0;
    }
  }

  if(FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) 
  {
    UL_ERROR((LOGID, "CoInitializeEx() failed. Exiting..."));
    cleanup_common();
    return 0;
  }

  //////////////////////Allocate DeviceDatabase//////////////////////////////////////////

  DeviceDatabase = new DevRecord[MAX_RECORDS_IN_DEVICE_DB];

  memset(DeviceDatabase, 0x00, sizeof(DevRecord)*MAX_RECORDS_IN_DEVICE_DB);

  //Load DeviceDatabase from sqlite db///////////////////////////////////////////////////
  ///sqlite ///////////////////
	sqlite3 *db;
	char *zErrMsg = 0;
	int rc;
	/////////////////////////////
	int n_rows = 0;
	int m_columns = 0;
	char program_path[_MAX_PATH];
	char db_file[_MAX_FNAME+_MAX_PATH];

	program_path[0] = '\0';

	if(GetModuleFileName(NULL, program_path, _MAX_PATH))
	{
		*(strrchr(program_path, '\\')) = '\0';        // Strip \\filename.exe off path
		*(strrchr(program_path, '\\')) = '\0';        // Strip \\bin off path
    }

	strcpy(db_file, program_path);

	strcat(db_file, "\\project\\opc_server_da.db");
	
	rc = sqlite3_open(db_file, &db);

	if(rc)
	{
	  fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
	  fflush(stderr);
	  sqlite3_close(db);
	  return 1;
	}
	
	gl_row_counter = 0;

	rc = sqlite3_exec(db, "select * from signals_measures_table;", callback, 0, &zErrMsg);

	if(rc != SQLITE_OK)
	{
	  fprintf(stderr, "SQL error: %s\n", zErrMsg);
	  fflush(stderr);
	  sqlite3_free(zErrMsg);
	}

	sqlite3_close(db);

	n_rows = gl_row_counter;
	m_columns = gl_column_counter;

  //////////////////////////////////end read db/////////////////////////////////////////////////////

  /////////////////Allocate device//////////////////////////////
  devp = new DEV();

  devp->idnum = 1;
  //////////////////////////////////////////////////////////////

  #ifdef USE_RIPC_MIDDLEWARE
	factory1 = RIPCClientFactory::getInstance();
	factory2 = RIPCClientFactory::getInstance();
	session1 = factory1->create("localhost", 6000);
	session2 = factory2->create("localhost", 6000);
	queue_monitor_dir = session1->createQueue("fifo_global_monitor_direction");
	queue_control_dir = session2->createQueue("fifo_global_control_direction");
  #endif

  //////////////////////init fifos//////////////////////////////

	/////////////////////Middleware/////////////////////////////////////////////////////////////////
	int32_t                 strength = 1;
	NtpTime                 persistence, deadline, minimumSeparation, delay;
	IPAddress				smIPAddress = IPADDRESS_INVALID;
	ORTEDomainProp          dp; 
	ORTEDomainAppEvents     events;
	
	publisher = NULL;
	subscriber = NULL;

	ORTEInit();
	ORTEDomainPropDefaultGet(&dp);
	NTPTIME_BUILD(minimumSeparation,0); 
	NTPTIME_BUILD(delay,1); //1s

	//initiate event system
	ORTEDomainInitEvents(&events);

	events.onRegFail = onRegFail;

	//Create application     
	domain = ORTEDomainAppCreate(ORTE_DEFAULT_DOMAIN,&dp,&events,ORTE_FALSE);

	iec_item_type_type_register(domain);

	//Create publisher
	NTPTIME_BUILD(persistence,5);

	publisher = ORTEPublicationCreate(
	domain,
	"fifo_global_control_direction",
	"iec_item_type",
	&instanceSend,
	&persistence,
	strength,
	NULL,
	NULL,
	NULL);

	//if(publisher == NULL){} //check this error
			
	//Create subscriber
	NTPTIME_BUILD(deadline,3);

	subscriber = ORTESubscriptionCreate(
	domain,
	IMMEDIATE,
	BEST_EFFORTS,
	"fifo_global_monitor_direction",
	"iec_item_type",
	&instanceRecv,
	&deadline,
	&minimumSeparation,
	recvCallBack,
	NULL,
	smIPAddress);

	//if(subscriber == NULL){} //check this error
	///////////////////////////////////Middleware//////////////////////////////////////////////////

  	char fifo_monitor_direction_name[70];
	char fifo_control_direction_name[70];

    fifo_monitor_direction_name[0] = '\0';
	fifo_control_direction_name[0] = '\0';

    strcpy(fifo_monitor_direction_name, "fifo_m_dir");
	strcpy(fifo_control_direction_name, "fifo_c_dir");

    fifo_monitor_direction = fifo_open(fifo_monitor_direction_name, MAX_FIFO_SIZE, iec_call_exit_handler);

	if(fifo_monitor_direction == NULL)
	{
		fprintf(stderr,"fifo_monitor_direction == NULL\n");
		fflush(stderr);
		return 0;
	}

	fifo_control_direction = fifo_open(fifo_control_direction_name, MAX_FIFO_SIZE, iec_call_exit_handler);

	if(fifo_control_direction == NULL)
	{
		fprintf(stderr,"fifo_control_direction == NULL\n");
		fflush(stderr);
		return 0;
	}

	unsigned long threadid;
	CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(control_dir_consumer), NULL, 0, &threadid);
	
	#ifdef USE_RIPC_MIDDLEWARE
	CreateThread(NULL, 0, LPTHREAD_START_ROUTINE(monitoring_dir_consumer), NULL, 0, &threadid);
	#endif
	//////////////////////////////////////////////////////////////

    //////////////////////////OPC server time stamp hour adjustment//////////////////////
//	char pAdjustHour[500+1];
//
//	int ret = GetPrivateProfileString(pItem,"AdjustHour","",pAdjustHour,500,pInitFile);
//
//	if(ret != 0)
//	{
//		adjust_hour = atoi(pAdjustHour);
//	}
    /////////////////////////////////////////////////////////////////////////////////////

  if(driver_init(0)) 
  { 
    CoUninitialize();
    cleanup_common();
    return 0;
  }

  if (FAILED(CoRegisterClassObject(GID_EnscadaOPCserverExe, &our_CF, 
				   CLSCTX_LOCAL_SERVER|
				   CLSCTX_REMOTE_SERVER|
				   CLSCTX_INPROC_SERVER, 
				   REGCLS_MULTIPLEUSE, &objid)))
  {
      UL_ERROR((LOGID, "CoRegisterClassObject() failed. Exiting..."));
      cleanup_all(objid);
      return 0;
  }

  Sleep(3000);
  our_CF.Release(); /* avoid locking by CoRegisterClassObject() */

  if (OPCstatus != OPC_STATUS_RUNNING) 
  {
    while(our_CF.in_use())
      Sleep(1000);
    cleanup_all(objid);
    return 0;
  }

  while(our_CF.in_use())
  {
    poll_device();
  }
  
 cleanup_all(objid);

 return 0;
}

/***************************************************************************
	       The Process Data to be exported via OPC
***************************************************************************/

static CRITICAL_SECTION lk_values; /* protects ti[] from simultaneous access */

int tTotal;
static loTagId *ti;
static char **tn;
static loTagValue *tv;

static void get_local_host_time(struct cp56time2a* time);

int WriteTags(const loCaller *ca,
              unsigned count, loTagPair taglist[],
              VARIANT values[], HRESULT error[], HRESULT *master, LCID lcid)
{
	unsigned i, ii, ei, devi;
	VARIANT v;
	char cmdData[DEV_DATALEN_MAX+1];
	char ldm;
	struct lconv *lcp;

	lcp = localeconv();
	ldm = *(lcp->decimal_point);
	VariantInit(&v);

	UL_TRACE((LOGID, "WriteTags(%d) invoked", count));

	EnterCriticalSection(&lk_values);

	for(ii = 0; ii < count; ii++) 
	{
		HRESULT hr = S_OK;
		loTagId clean = 0;
		cmdData[0] = '\0';
		cmdData[DEV_DATALEN_MAX] = '\0';

		UL_TRACE((LOGID,  "WriteTags(Rt=%u Ti=%u)", taglist[ii].tpRt, taglist[ii].tpTi));
		i = (unsigned)taglist[ii].tpRt - 1;
		ei = i % devp->cv_size;
		devi = i / devp->cv_size;

		if (!taglist[ii].tpTi || !taglist[ii].tpRt || i >= (unsigned)tTotal)
		continue;

		VARTYPE tvVt = tv[i].tvValue.vt;
		hr = VariantChangeType(&v, &values[ii], 0, tvVt);
		char *dm;

		if(hr == S_OK) 
		{
			switch (tvVt) 
			{
				case VT_BOOL:
				{
					sprintf(cmdData, "%d", v.boolVal);
				}
				break;
				case VT_I2:
				{
					sprintf(cmdData, "%d", v.iVal);
				}
				break;
				case VT_UI2:
				{
					sprintf(cmdData, "%d", v.uiVal);
				}
				break;
				case VT_UI4:
				{
					sprintf(cmdData, "%d", v.ulVal);
				}
				break;
				case VT_R4:
				{
					sprintf(cmdData, "%f", v.fltVal);
					if (ldm != '.' && (dm = strchr(cmdData, ldm)))
					*dm = '.';
				}
				break;
				case VT_BSTR:
				default:
				{
					WideCharToMultiByte(CP_ACP,
					0,
					v.bstrVal,
					-1,
					cmdData,
					DEV_DATALEN_MAX,
					NULL, NULL);
				}
			}

			UL_TRACE((LOGID, "cmdData=%s, ioa=%d", cmdData, DeviceDatabase[ei].ioa_control_center));
			
			//Send C_SE_TC_1//////////////////////////////////////////////////////////////////////////
			struct iec_item item_to_send;
			memset(&item_to_send,0x00, sizeof(struct iec_item));
			item_to_send.iec_type = C_SE_TC_1;
			item_to_send.iec_obj.ioa = DeviceDatabase[ei].ioa_control_center;
			item_to_send.iec_obj.o.type63.sv = (float)atof(cmdData);

			struct cp56time2a iec_cmd_time;
			get_local_host_time(&iec_cmd_time);
			item_to_send.iec_obj.o.type63.time = iec_cmd_time;

			item_to_send.msg_id = n_msg_sent_control_dir;
			item_to_send.checksum = clearCrc((unsigned char *)&item_to_send, sizeof(struct iec_item));
			fifo_put(fifo_control_direction, (char*)&item_to_send, sizeof(struct iec_item));
			///////////////////////////////////////////////////////////////////////////////////////////
		}

		VariantClear(&v);

		if (S_OK != hr) 
		{
			*master = S_FALSE;
			error[ii] = hr;

			UL_TRACE((LOGID, "%!l WriteTags(Rt=%u Ti=%u %s %s)", hr, taglist[ii].tpRt, taglist[ii].tpTi, tn[i], cmdData));
		}

		taglist[ii].tpTi = clean; /* clean if ok */
	}

	LeaveCriticalSection(&lk_values);

	return loDW_TOCACHE; /* put to the cache all tags unhandled here */
	// loDW_ALLDONE; 
}


loTrid ReadTags(const loCaller *,
		unsigned  count,
		loTagPair taglist[],
		VARIANT   values[],
		WORD      qualities[],
		FILETIME  stamps[],
		HRESULT   errs[],
		HRESULT  *master_err,
		HRESULT  *master_qual,
		const VARTYPE vtype[],
		LCID lcid
		)
{
  return loDR_STORED;
}


void activation_monitor(const loCaller *ca, int count, loTagPair *til)
{
  int act = 1;
  if (0 > count)
    act = 0, count = -count;
  while(count--)
    {
      UL_DEBUG((LOGID, "MON: %u %s %s", til[count].tpTi,
                tn[(int) til[count].tpRt], act ? "On" : "Off"));
    }
}

int init_tags(void)
{
  int devi, i;
  unsigned rights;
  LONG ecode =  S_OK;
  FILETIME ft;

  GetSystemTimeAsFileTime(&ft);

  EnterCriticalSection(&lk_values);

  ti = new loTagId[tTotal];
  tv = new loTagValue[tTotal];
  tn = new char*[tTotal];
  
  for(i = 0; i < tTotal; i++)
  {
    tn[i] = new char[TAGNAME_LEN];
  }
      
  for(devi = 0, i = 0; devi < devp->idnum; devi++) 
  {
    int ei;
    for(ei = 0; ei < devp->cv_size && !ecode; ei++, i++) 
	{
      int id = devp->cv_id[ei];
      
	  sprintf(tn[i], "%s",  DeviceDatabase[id].name);

      rights = (OPC_READABLE | OPC_WRITEABLE);

      VariantInit(&tv[i].tvValue);

      switch (DeviceDatabase[ei].dtype) 
	  {
		  case VT_BOOL:
		  {
				V_BOOL(&tv[i].tvValue) = 0;
				V_VT(&tv[i].tvValue) = VT_BOOL;
				ecode = loAddRealTag_a(our_service,
							   &ti[i], /* returned TagId */
							   (loRealTag)(i+1), /* != 0 */
							   tn[i],
							   0, /* loTF_XXX */
							   rights,
							   &tv[i].tvValue,
							   0,
							   1);
		  }
		  break;
		  case VT_I2:
		  {
				V_I2(&tv[i].tvValue) = 0;
				V_VT(&tv[i].tvValue) = VT_I2;
				ecode = loAddRealTag_a(our_service,
							   &ti[i], /* returned TagId */
							   (loRealTag)(i+1), /* != 0 */
							   tn[i],
							   0, /* loTF_XXX */
							   rights,
							   &tv[i].tvValue,
							   -99999,
							   99999);
		  }
		  break;
		  case VT_UI2:
		  {
				V_UI2(&tv[i].tvValue) = 0;
				V_VT(&tv[i].tvValue) = VT_UI2;
				ecode = loAddRealTag_a(our_service,
							   &ti[i], 
							   (loRealTag)(i+1), 
							   tn[i],
							   0, 
							   rights,
							   &tv[i].tvValue,
							   0,
							   99999);
		  }
		  break;
		  case VT_UI4:
		  {
				V_UI4(&tv[i].tvValue) = 0;
				V_VT(&tv[i].tvValue) = VT_UI4;
				ecode = loAddRealTag_a(our_service,
							   &ti[i], /* returned TagId */
							   (loRealTag)(i+1), /* != 0 */
							   tn[i],
							   0, /* loTF_XXX */
							   rights,
							   &tv[i].tvValue,
							   0,
							   99999);
		  }
		  break;
		  case VT_R4:
		  {
				V_R4(&tv[i].tvValue) = 0.0;
				V_VT(&tv[i].tvValue) = VT_R4;
				ecode = loAddRealTag_a(our_service,
							   &ti[i], /* returned TagId */
							   (loRealTag)(i+1), /* != 0 */
							   tn[i],
							   0, /* loTF_XXX */
							   rights,
							   &tv[i].tvValue,
							   -99999.0,
							   99999.0);
		  }
		  break;
		  case VT_BSTR:
		  default:
		  {
				V_BSTR(&tv[i].tvValue) = SysAllocString(L"");
				V_VT(&tv[i].tvValue) = VT_BSTR;
				ecode = loAddRealTag(our_service,
							 &ti[i], /* returned TagId */
							 (loRealTag)(i+1), /* != 0 */
							 tn[i],
							 0, /* loTF_XXX */
							 rights,
							 &tv[i].tvValue,
							 0,
							 0);
		  }
      }

      UL_TRACE((LOGID, "%!e loAddRealTag(%s) = %u", ecode, tn[i], ti[i]));

      tv[i].tvTi = ti[i];
      tv[i].tvState.tsTime = ft;
      tv[i].tvState.tsError = S_OK;
      tv[i].tvState.tsQuality = OPC_QUALITY_NOT_CONNECTED;

    }
  }

  LeaveCriticalSection(&lk_values);

  if(ecode) 
  {
    UL_ERROR((LOGID, "%!e driver_init()=", ecode));
    return -1;
  }
  return 0;
}

#include <signal.h>

char* get_date_time()
{
	static char sz[128];
	time_t t = time(NULL);
	struct tm *ptm = localtime(&t);
	
	strftime(sz, sizeof(sz)-2, "%m/%d/%y %H:%M:%S", ptm);

	strcat(sz, "|");
	return sz;
}

void iec_call_exit_handler(int line, char* file, char* reason)
{
	FILE* fp;
	char program_path[_MAX_PATH];
	char log_file[_MAX_FNAME+_MAX_PATH];

#ifdef WIN32
	if(GetModuleFileName(NULL, program_path, _MAX_PATH))
	{
		*(strrchr(program_path, '\\')) = '\0';        // Strip \\filename.exe off path
		*(strrchr(program_path, '\\')) = '\0';        // Strip \\bin off path
    }
#elif __unix__
	if(getcwd(program_path, _MAX_PATH))
	{
		*(strrchr(program_path, '/')) = '\0';        // Strip \\filename.exe off path
		*(strrchr(program_path, '/')) = '\0';        // Strip \\bin off path
    }
#endif

	strcpy(log_file, program_path);

#ifdef WIN32
	strcat(log_file, "\\logs\\fifo_opc_server_da.log");
#elif __unix__
	strcat(log_file, "/logs/fifo_opc_server_da.log");	
#endif

	fp = fopen(log_file, "a");

	if(fp)
	{
		if(line && file && reason)
		{
			fprintf(fp, "PID:%d time:%s exit process at line: %d, file %s, reason:%s\n", GetCurrentProcessId, get_date_time(), line, file, reason);
		}
		else if(line && file)
		{
			fprintf(fp, "PID:%d time:%s exit process at line: %d, file %s\n", GetCurrentProcessId, get_date_time(), line, file);
		}
		else if(reason)
		{
			fprintf(fp, "PID:%d time:%s exit process for reason %s\n", GetCurrentProcessId, get_date_time(), reason);
		}

		fflush(fp);
		fclose(fp);
	}

	raise(SIGABRT);   //raise abort signal which in turn starts automatically a separete thread and call iec101SignalHandler
}

#include <time.h>
#include <sys/timeb.h>

void get_local_host_time(struct cp56time2a* time)
{
	struct timeb tb;
	struct tm	*ptm;

    ftime (&tb);
	ptm = localtime(&tb.time);
		
	time->hour = ptm->tm_hour;					//<0.23>
	time->min = ptm->tm_min;					//<0..59>
	time->msec = ptm->tm_sec*1000 + tb.millitm; //<0.. 59999>
	time->mday = ptm->tm_mday; //<1..31>
	time->wday = (ptm->tm_wday == 0) ? ptm->tm_wday + 7 : ptm->tm_wday; //<1..7>
	time->month = ptm->tm_mon + 1; //<1..12>
	time->year = ptm->tm_year - 100; //<0.99>
	time->iv = 0; //<0..1> Invalid: <0> is valid, <1> is invalid
	time->su = (u_char)tb.dstflag; //<0..1> SUmmer time: <0> is standard time, <1> is summer time

    return;
}


void get_utc_host_time(struct cp56time2a* time)
{
	struct timeb tb;
	struct tm	*ptm;

    ftime (&tb);
	ptm = gmtime(&tb.time);
		
	time->hour = ptm->tm_hour;					//<0..23>
	time->min = ptm->tm_min;					//<0..59>
	time->msec = ptm->tm_sec*1000 + tb.millitm; //<0..59999>
	time->mday = ptm->tm_mday; //<1..31>
	time->wday = (ptm->tm_wday == 0) ? ptm->tm_wday + 7 : ptm->tm_wday; //<1..7>
	time->month = ptm->tm_mon + 1; //<1..12>
	time->year = ptm->tm_year - 100; //<0.99>
	time->iv = 0; //<0..1> Invalid: <0> is valid, <1> is invalid
	time->su = (u_char)tb.dstflag; //<0..1> SUmmer time: <0> is standard time, <1> is summer time

    return;
}

signed __int64 epoch_from_cp56time2a(const struct cp56time2a* time)
{
	struct tm	t;
	signed __int64 epoch_in_millisec;
	int ms;
    time_t sec;
	
	memset(&t, 0x00, sizeof(struct tm));
	
	t.tm_hour = time->hour;
	t.tm_min = time->min;
	t.tm_sec = time->msec/1000;
	ms = time->msec%1000; //not used
	t.tm_mday = time->mday;
	t.tm_mon = time->month - 1;	  //from <1..12> to	<0..11>				
	t.tm_year = time->year + 100; //from <0..99> to <years from 1900>
	t.tm_isdst = time->su;
	
	sec = mktime(&t);

	if((sec == -1) || (time->iv == 1))
	{
		sec = 0;
	}

    epoch_in_millisec =  (signed __int64)sec;

	epoch_in_millisec =  epoch_in_millisec*1000 + ms;

	return epoch_in_millisec;
}

int error_producer = ERROR_INVALID_DATA;

int update_producer(void)
{
	//This code runs inside main thread

	//cp56time2a time;
	//signed __int64 epoch_in_millisec;
    //int exit_loop = 0;
    FILETIME ft;
    struct cp56time2a message_time;

	unsigned char buf[sizeof(struct iec_item)];
	int len;
	const unsigned wait_limit_ms = 1;
	struct iec_item* p_item;

//	for(int ei = 0; ei < devp->cv_size; ei++) 
//	{
//		DeviceDatabase[ei].updated_in_this_load = 0;	 //flag as not updated
//	}

	for(int i = 0; (len = fifo_get(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms)) >= 0; i += 1)	
	{ 
		p_item = (struct iec_item*)buf;
			
		printf("Receiving %d th message \n", p_item->msg_id);
		
		UL_TRACE((LOGID, "Receiving %d th message", p_item->msg_id));
		UL_TRACE((LOGID, "p_item->iec_type %d", p_item->iec_type));
		UL_TRACE((LOGID, "p_item->iec_obj.ioa %d", p_item->iec_obj.ioa));
				
//		printf("Receiving %d th iec101 message from line = %d\n", p_item->msg_id, instanceID + 1);

		//for (int j = 0; j < len; j++) 
		//{ 
			//assert((unsigned char)buf[i] == len);
			//unsigned char c = *((unsigned char*)buf + j);
			//printf("rx <--- 0x%02x-\n", c);
			//fprintf(fp,"rx <--- 0x%02x-\n", c);
			//fflush(fp);

			//IT_COMMENT1("rx <--- 0x%02x-\n", c);
		//}

		//printf("---------------\n");

		unsigned char rc = clearCrc((unsigned char *)buf, sizeof(struct iec_item));
		if(rc != 0)
		{
			ExitProcess(1);
		}

		switch(p_item->iec_type)
		{
			case M_SP_NA_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type1.sp;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}
                        
                        ////////////time stamp////////////////////////////////////////
	                    get_local_host_time(&message_time);

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type1.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_DP_NA_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
                    if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type3.dp;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
	                    get_local_host_time(&message_time);

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type3.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			//case M_BO_NA_1:
			//{
			//}
			//break;
			case M_ME_NA_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type9.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
	                    get_local_host_time(&message_time);

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type9.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_ME_NB_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type11.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
	                    get_local_host_time(&message_time);

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type11.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_ME_NC_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type13.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
	                    get_local_host_time(&message_time);

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type13.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_SP_TB_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type30.sp;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type30.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type30.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_DP_TB_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type31.dp;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type31.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type31.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_BO_TB_1:
			{
				//sprintf("%d", p_item->iec_obj.o.type33.stcd);
			}
			break;
			case M_ME_TD_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type34.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type34.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type34.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_ME_TE_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type35.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type35.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type35.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_ME_TF_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type36.mv;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}

                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type36.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type36.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case M_IT_TB_1:
			{
				for(int j = 0; j < devp->cv_size; j++) 
				{
					if(DeviceDatabase[j].ioa_control_center == p_item->iec_obj.ioa)
					{
						//if(DeviceDatabase[j].updated_in_this_load == 0)
						//{
							DeviceDatabase[j].current_value = p_item->iec_obj.o.type37.counter;
						//	DeviceDatabase[j].updated_in_this_load = 1;

						//	fifo_remove_peeked(fifo_monitor_direction, (char*)buf, sizeof(struct iec_item), wait_limit_ms);
						//}
						//else
						//{
						//	exit_loop = 1;
						//	break;
						//}
						
                        ////////////time stamp////////////////////////////////////////
                        message_time = p_item->iec_obj.o.type37.time;

	                    signed __int64 time_in_mseconds = epoch_from_cp56time2a(&message_time);

                        epocToFileTime(time_in_mseconds, &ft);

                        DeviceDatabase[j].ft = ft;
                        //////////////////////////////////////////////////////////////

                        if(p_item->iec_obj.o.type37.iv == 0)
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_GOOD;
                        }
                        else
                        {
                            DeviceDatabase[j].Quality = OPC_QUALITY_BAD;
                        }

                        error_producer = ERROR_SUCCESS;

						break;
					}
				}
			}
			break;
			case C_IC_NA_1:
			{
				
			}
			break;
			case C_CS_NA_1:
			{
                
			}
			break;
            case C_LO_ST_1:
			{
                printf("IEC 101 has not link\n");
                error_producer = ERROR_INVALID_DATA;
			}
			break;
			default:
			{
				printf("Not supported type%d \n", p_item->iec_type);
			}
			break;
		}

		//if(exit_loop)
		//{
		//	break;
		//}

		if(i > 50)
		{
			break;
		}
	}

    return error_producer;
}

void poll_device(void)
{
  LONG ecode = 0;
  int devi, i;

  //Get data from real time database or from hardware driver

  for(devi = 0, i = 0; devi < devp->idnum; devi++) 
  {
	ecode = update_producer();

    EnterCriticalSection(&lk_values);

    int ei;
    for (ei = 0; ei < devp->cv_size; ei++, i++)
	{
      WCHAR buf[64];
      //LCID lcid = MAKELCID(0x0409, SORT_DEFAULT);

      //if(ecode == ERROR_SUCCESS)
	  //{
		  VARTYPE tvVt = tv[i].tvValue.vt;
		  VariantClear(&tv[i].tvValue);

		  switch (tvVt) 
		  {
			  case VT_BOOL:
				V_BOOL(&tv[i].tvValue)= (short)DeviceDatabase[ei].current_value;
				break;
			  case VT_I2:
                V_I2(&tv[i].tvValue)= (short)DeviceDatabase[ei].current_value;
				break;
			   case VT_UI2:
				 V_UI2(&tv[i].tvValue)= (unsigned short)DeviceDatabase[ei].current_value;
                break;
			  case VT_UI4:
                V_UI4(&tv[i].tvValue)= (unsigned int)DeviceDatabase[ei].current_value;
				break;
			  case VT_R4:
                V_R4(&tv[i].tvValue) = (float)DeviceDatabase[ei].current_value;
				break;
			  case VT_BSTR:
			  default:
			  {
				 char buf1[100];
				 itoa((int)DeviceDatabase[ei].current_value, buf1, 10);
				 MultiByteToWideChar(CP_ACP, 0, buf1, strlen(buf1) + 1, buf, sizeof(buf)/sizeof(buf[0]));
				 V_BSTR(&tv[i].tvValue) = SysAllocString(buf);
			  }
		  }

		  V_VT(&tv[i].tvValue) = tvVt;

		  //if(VariantChangeType(&tv[i].tvValue, &tv[i].tvValue, 0, tvVt))
		  //UL_ERROR((LOGID, "%!L VariantChangeType(%s)", tn[i]));

		  tv[i].tvState.tsQuality = DeviceDatabase[ei].Quality;
		//} 
		//else
		//{
        //  DeviceDatabase[ei].Quality = OPC_QUALITY_COMM_FAILURE;
		//	tv[i].tvState.tsQuality = OPC_QUALITY_COMM_FAILURE;
		//}

        tv[i].tvState.tsTime = DeviceDatabase[ei].ft;
    }

	//for(ei = 0; ei < devp->cv_size; ei++) 
	//{
	//	DeviceDatabase[ei].updated_in_this_load = 0;	 //flag as not updated
	//}

	/** MANDATORY: send all the values into the cache: */
    loCacheUpdate(our_service, tTotal, tv, 0);
    LeaveCriticalSection(&lk_values);
  }
  
  //Sleep(1000);
  Sleep(10);

}

int driver_init(int lflags)
{
  loDriver ld;
  int ecode;

  if (our_service) {
      UL_ERROR((LOGID, "Driver already initialized!"));
      return 0;
  }

  tTotal = devp->cv_size*devp->idnum;

  memset(&ld, 0, sizeof(ld));
  //set refresh rate according to reactivity of DEV
  ld.ldRefreshRate = 1000;
  ld.ldRefreshRate_min = 1000;
  ld.ldWriteTags = WriteTags;
  ld.ldReadTags = ReadTags;
  ld.ldSubscribe = activation_monitor;
  //ld.ldConvertTags = ConvertTags;
  //ld.ldAskItemID = AskItemID;
  ld.ldFlags = lflags | loDF_IGNCASE;
  ld.ldBranchSep = '.'; /* Hierarchial branch separator */

  ecode = loServiceCreate(&our_service, &ld, tTotal);
  UL_TRACE((LOGID, "%!e loServiceCreate()=", ecode));
  if (ecode) return -1;

  InitializeCriticalSection(&lk_values);
  if (init_tags()) return -1;

  return 0;
}

void driver_destroy(void)
{
  if (our_service)
    {
      int ecode = loServiceDestroy(our_service);
      UL_INFO((LOGID, "%!e loServiceDestroy(%p) = ", ecode));

      for(ecode = 0; ecode < tTotal; ecode++)
        VariantClear(&tv[ecode].tvValue);

      for(ecode = 0; ecode < tTotal; ecode++)
	delete[] tn[ecode];

      delete[] ti;
      delete[] tn;
      delete[] tv;


      DeleteCriticalSection(&lk_values);

      our_service = 0;
    }
}

int show_error(LPCTSTR msg)
{
  fprintf(stderr, "%s", msg);
  fflush(stderr);
  return 1;
}

int show_msg(LPCTSTR msg)
{
  fprintf(stderr, "%s", msg);
  fflush(stderr);
  return 1;
}

